#!/usr/bin/env python3
"""
特征重要性分析服务
基于SHAP/LIME/FI分析结果判断气象因素的影响程度
"""

import numpy as np
import pandas as pd
import logging
from typing import Dict, List, Optional, Tuple
from datetime import datetime, timedelta
import joblib
import os

logger = logging.getLogger(__name__)


class FeatureImportanceService:
    """特征重要性分析服务"""
    
    def __init__(self):
        self.model_dir = "backend/models/saved_models"
        self.feature_names = [
            # 历史负荷特征 (672维)
            *[f"load_lag_{i}" for i in range(672)],
            # 统计特征 (9维)
            "load_mean", "load_std", "load_max", "load_min", "load_median",
            "load_p10", "load_p90", "load_p25", "load_p75",
            # 日间统计特征 (21维)
            *[f"daily_mean_{i}" for i in range(7)],
            *[f"daily_max_{i}" for i in range(7)],
            *[f"daily_min_{i}" for i in range(7)],
            # 时间特征 (9维)
            "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday",
            "is_holiday", "is_weekend",
            # 气象特征 (4维)
            "temp_max", "temp_min", "humidity", "weather_temp_max"
        ]
        
        # 气象特征索引
        self.weather_feature_indices = {
            'temp_max': 711,   # 最高温度 (匹配数据库)
            'temp_min': 712,   # 最低温度 (匹配数据库)
            'weather_temp_max': 714,  # 新格式的最高温度
            'weather_temp_min': 715,  # 新格式的最低温度
            'humidity': 713    # 湿度 (匹配)
        }
        
        # 影响程度阈值配置
        self.impact_thresholds = {
            'high': 0.15,    # 高影响：特征重要性 > 15%
            'medium': 0.08,  # 中等影响：8% - 15%
            'low': 0.03      # 低影响：3% - 8%
        }
    
    def analyze_feature_importance(self, model_name: str, target_date: str) -> Dict:
        """分析特征重要性并判断气象因素的影响程度"""
        try:
            # 获取数据库中的特征重要性数据
            feature_importance_data = self._get_feature_importance_from_db(model_name, target_date)
            
            if not feature_importance_data:
                return self._get_default_weather_analysis()
            
            # 分析气象特征的重要性（传统方法）
            weather_importance = self._analyze_weather_importance_from_db(feature_importance_data)
            
            # 根据重要性分数判断影响程度
            weather_impact = self._determine_weather_impact(weather_importance)
            
            # 获取目标日期的天气数据
            weather_data = self._get_target_date_weather(target_date)
            
            # 生成详细的分析报告
            analysis_report = self._generate_analysis_report(
                weather_importance, weather_impact, weather_data
            )
            
            # 添加前端可视化需要的数据
            analysis_report['feature_importance_data'] = feature_importance_data
            
            return analysis_report
            
        except Exception as e:
            logger.error(f"特征重要性分析失败: {e}")
            return self._get_default_weather_analysis()
    
    def _get_feature_importance_from_db(self, model_name: str, target_date: str) -> List[Dict]:
        """从数据库获取特征重要性数据"""
        try:
            from backend.config.database import get_db_session
            from backend.entities.feat_imp import FeatImp
            from datetime import datetime, date
            
            # 解析目标日期
            target_dt = datetime.strptime(target_date, '%Y-%m-%d').date()
            
            with get_db_session() as db:
                # 获取指定模型和日期的特征重要性数据
                feat_records = db.query(FeatImp).filter(
                    FeatImp.model == model_name,
                    FeatImp.train_dt == target_dt
                ).all()
                
                # 如果没有当天数据，获取最近的数据
                if not feat_records:
                    feat_records = db.query(FeatImp).filter(
                        FeatImp.model == model_name
                    ).order_by(FeatImp.train_dt.desc()).limit(50).all()
                
                # 转换为前端需要的格式
                feature_data = []
                for record in feat_records:
                    feature_data.append({
                        'model': record.model,
                        'feat': record.feat,
                        'imp': float(record.imp),
                        'train_dt': record.train_dt.strftime('%Y-%m-%d') if record.train_dt else target_date
                    })
                
                # 同时获取所有相关模型的数据（LightGBM, XGBoost, ensemble）
                if model_name == 'ensemble':
                    # 对于ensemble，获取所有子模型的数据
                    for sub_model in ['lightgbm', 'xgboost']:
                        sub_records = db.query(FeatImp).filter(
                            FeatImp.model == sub_model,
                            FeatImp.train_dt == target_dt
                        ).all()
                        
                        if not sub_records:
                            sub_records = db.query(FeatImp).filter(
                                FeatImp.model == sub_model
                            ).order_by(FeatImp.train_dt.desc()).limit(50).all()
                        
                        for record in sub_records:
                            feature_data.append({
                                'model': record.model,
                                'feat': record.feat,
                                'imp': float(record.imp),
                                'train_dt': record.train_dt.strftime('%Y-%m-%d') if record.train_dt else target_date
                            })
                
                elif model_name in ['lightgbm', 'xgboost']:
                    # 对于单模型，也获取ensemble的权重数据
                    ensemble_records = db.query(FeatImp).filter(
                        FeatImp.model == 'ensemble',
                        FeatImp.train_dt == target_dt
                    ).all()
                    
                    if not ensemble_records:
                        ensemble_records = db.query(FeatImp).filter(
                            FeatImp.model == 'ensemble'
                        ).order_by(FeatImp.train_dt.desc()).limit(10).all()
                    
                    for record in ensemble_records:
                        feature_data.append({
                            'model': record.model,
                            'feat': record.feat,
                            'imp': float(record.imp),
                            'train_dt': record.train_dt.strftime('%Y-%m-%d') if record.train_dt else target_date
                        })
                
                logger.info(f"从数据库获取到 {len(feature_data)} 条特征重要性数据")
                return feature_data
                
        except Exception as e:
            logger.error(f"从数据库获取特征重要性数据失败: {e}")
            return []
    
    def _analyze_weather_importance_from_db(self, feature_data: List[Dict]) -> Dict:
        """从数据库数据分析气象特征重要性"""
        try:
            weather_importance = {
                'temp_max': 0.0,
                'temp_min': 0.0,
                'humidity': 0.0,
                'precip': 0.0,
                'total': 0.0
            }
            
            # 调试：记录所有特征名称
            all_features = [item['feat'] for item in feature_data]
            weather_features = [feat for feat in all_features if any(keyword in feat.lower() for keyword in ['weather', 'temp', 'hum', 'precip', 'rain'])]
            logger.info(f"检测到的气象相关特征: {weather_features[:10]}")  # 只显示前10个
            
            # 从特征数据中提取气象特征（支持多种命名格式）
            weather_count = 0
            for item in feature_data:
                feat_name = item['feat']
                importance = item['imp']
                
                # 匹配各种可能的气象特征命名
                if feat_name in ['weather_temp_max', 'temp_max', 't_max'] or 'temp_max' in feat_name:
                    weather_importance['temp_max'] += importance
                    weather_count += 1
                elif feat_name in ['weather_temp_min', 'temp_min', 't_min'] or 'temp_min' in feat_name:
                    weather_importance['temp_min'] += importance
                    weather_count += 1
                elif feat_name in ['weather_humidity', 'humidity', 'hum'] or 'humidity' in feat_name:
                    weather_importance['humidity'] += importance
                    weather_count += 1
                elif feat_name in ['weather_precip', 'precip', 'precipitation', 'rain'] or any(kw in feat_name for kw in ['precip', 'rain']):
                    weather_importance['precip'] += importance
                    weather_count += 1
            
            logger.info(f"匹配到的气象特征数量: {weather_count}")
            logger.info(f"气象特征重要性分布: {weather_importance}")
            
            weather_importance['total'] = sum([
                weather_importance['temp_max'],
                weather_importance['temp_min'], 
                weather_importance['humidity'],
                weather_importance['precip']
            ])
            
            return weather_importance
            
        except Exception as e:
            logger.error(f"分析气象重要性失败: {e}")
            return {'temp_max': 0.0, 'temp_min': 0.0, 'humidity': 0.0, 'precip': 0.0, 'total': 0.0}
    
    def _get_model_feature_importance(self, model_name: str) -> Optional[np.ndarray]:
        """获取模型的特征重要性分数"""
        try:
            # 定义可能的模型文件格式
            model_files = {
                'lightgbm': ['lightgbm_model.pkl'],
                'xgboost': ['xgboost_model.pkl'],
                # MLP模型已移除
                'ensemble': ['ensemble_config.json']  # 添加ensemble支持
            }
            
            if model_name not in model_files:
                logger.warning(f"不支持的模型类型: {model_name}")
                return None
            
            # 对于ensemble模型，尝试从数据库获取特征重要性
            if model_name == 'ensemble':
                return self._get_ensemble_feature_importance()
            
            # 尝试加载模型文件
            for filename in model_files[model_name]:
                model_path = os.path.join(self.model_dir, filename)
                
                if os.path.exists(model_path):
                    try:
                        # MLP模型已移除，只保留LightGBM和XGBoost
                        # lightgbm和xgboost模型
                        model_data = joblib.load(model_path)
                        # 检查是否是字典格式
                        if isinstance(model_data, dict) and 'model' in model_data:
                            model = model_data['model']
                            logger.info(f"从字典中提取模型对象: {type(model)}")
                        else:
                            model = model_data
                        
                        # 获取特征重要性
                        if hasattr(model, 'feature_importances_'):
                            feature_importances = model.feature_importances_
                            if feature_importances is not None and len(feature_importances) > 0:
                                logger.info(f"成功获取 {model_name} 特征重要性，特征数量: {len(feature_importances)}")
                                return feature_importances
                            else:
                                logger.warning(f"模型 {model_name} 的特征重要性为空")
                                return None
                        elif hasattr(model, 'estimators_'):
                            # 对于MultiOutputRegressor，从estimators获取特征重要性
                            estimators = model.estimators_
                            if estimators and len(estimators) > 0:
                                # 计算所有estimator的特征重要性平均值
                                importance_scores = np.zeros(len(estimators[0].feature_importances_))
                                valid_estimators = 0
                                for estimator in estimators:
                                    if hasattr(estimator, 'feature_importances_'):
                                        importance_scores += estimator.feature_importances_
                                        valid_estimators += 1
                                
                                if valid_estimators > 0:
                                    importance_scores /= valid_estimators
                                    logger.info(f"成功获取 {model_name} MultiOutput特征重要性，特征数量: {len(importance_scores)}")
                                    return importance_scores
                                else:
                                    logger.warning(f"模型 {model_name} 的estimators没有特征重要性")
                                    return None
                            else:
                                logger.warning(f"模型 {model_name} 的estimators为空")
                                return None
                        elif hasattr(model, 'coef_'):
                            # 对于线性模型，使用系数的绝对值
                            coef = model.coef_
                            if coef is not None and len(coef) > 0:
                                logger.info(f"成功获取 {model_name} 系数，特征数量: {len(coef)}")
                                return np.abs(coef)
                            else:
                                logger.warning(f"模型 {model_name} 的系数为空")
                                return None
                        else:
                            logger.warning(f"模型 {model_name} 不支持特征重要性分析")
                            return None
                            
                    except Exception as e:
                        logger.error(f"加载模型文件失败 ({filename}): {e}")
                        continue
            
            logger.warning(f"模型文件不存在: {model_name}")
            return None
                
        except Exception as e:
            logger.error(f"获取模型特征重要性失败: {e}")
            return None
    
    def _get_ensemble_feature_importance(self) -> Optional[np.ndarray]:
        """获取集成模型的特征重要性（从数据库或默认值）"""
        try:
            from backend.config.database import get_db_session
            from backend.entities.feat_imp import FeatImp
            from datetime import datetime
            
            # 尝试从数据库获取最新的特征重要性数据
            with get_db_session() as db:
                # 获取最近的特征重要性数据
                recent_feat_imp = db.query(FeatImp).order_by(
                    FeatImp.train_dt.desc()
                ).limit(100).all()
                
                if recent_feat_imp:
                    # 计算平均特征重要性
                    feature_importance_dict = {}
                    for feat_imp in recent_feat_imp:
                        if feat_imp.feat is not None and feat_imp.imp is not None:
                            if feat_imp.feat not in feature_importance_dict:
                                feature_importance_dict[feat_imp.feat] = []
                            feature_importance_dict[feat_imp.feat].append(feat_imp.imp)
                    
                    # 计算平均值
                    avg_importance = {}
                    for feat, values in feature_importance_dict.items():
                        avg_importance[feat] = np.mean(values)
                    
                    # 转换为数组格式
                    importance_array = np.zeros(len(self.feature_names))
                    mapped_count = 0
                    for i, feat_name in enumerate(self.feature_names):
                        if feat_name in avg_importance:
                            importance_array[i] = avg_importance[feat_name]
                            mapped_count += 1
                    
                    logger.info(f"从数据库映射了 {mapped_count} 个特征到 {len(self.feature_names)} 个特征位置")
                    
                    # 如果映射的特征太少，使用默认值
                    if mapped_count < 10:  # 如果映射的特征少于10个，使用默认值
                        logger.warning(f"映射的特征数量太少 ({mapped_count})，使用默认特征重要性")
                        return self._get_default_feature_importance()
                    
                    logger.info(f"从数据库获取集成模型特征重要性，特征数量: {len(importance_array)}")
                    return importance_array
                else:
                    logger.warning("数据库中没有特征重要性数据，使用默认值")
                    return self._get_default_feature_importance()
                    
        except Exception as e:
            logger.error(f"获取集成模型特征重要性失败: {e}")
            return self._get_default_feature_importance()
    
    def _get_default_feature_importance(self) -> np.ndarray:
        """获取默认的特征重要性数组"""
        # 创建一个默认的特征重要性数组
        importance_array = np.zeros(len(self.feature_names))
        
        # 为气象特征设置默认重要性
        weather_defaults = {
            'temp_max': 0.08,    # 8%
            'temp_min': 0.06,    # 6%
            'weather_temp_max': 0.08,  # 8%
            'weather_temp_min': 0.06,  # 6%
            'humidity': 0.04  # 4%
        }
        
        for feature_name, default_importance in weather_defaults.items():
            if feature_name in self.weather_feature_indices:
                idx = self.weather_feature_indices[feature_name]
                if idx < len(importance_array):
                    importance_array[idx] = default_importance
        
        # 为历史负荷特征设置默认重要性（占大部分）
        historical_importance = 0.75  # 75%
        historical_count = 672  # 历史负荷特征数量
        if historical_count < len(importance_array):
            importance_array[:historical_count] = historical_importance / historical_count
        else:
            # 如果历史特征数量超过数组长度，调整分配
            importance_array[:] = historical_importance / len(importance_array)
        
        # 为时间特征设置默认重要性
        time_importance = 0.10  # 10%
        time_start = 706  # 时间特征开始索引
        time_count = 9    # 时间特征数量
        if time_start + time_count <= len(importance_array):
            importance_array[time_start:time_start + time_count] = time_importance / time_count
        
        logger.info(f"使用默认特征重要性，总特征数: {len(importance_array)}")
        return importance_array
    
    def _analyze_weather_importance(self, importance_scores: np.ndarray) -> Dict[str, float]:
        """分析气象特征的重要性"""
        weather_importance = {}
        
        for feature_name, feature_idx in self.weather_feature_indices.items():
            if feature_idx < len(importance_scores):
                # 计算相对重要性（占总重要性的百分比）
                relative_importance = importance_scores[feature_idx] / np.sum(importance_scores)
                weather_importance[feature_name] = relative_importance
            else:
                weather_importance[feature_name] = 0.0
        
        return weather_importance
    
    def _determine_weather_impact(self, weather_importance: Dict[str, float]) -> Dict[str, str]:
        """根据重要性分数判断影响程度"""
        impact_levels = {}
        
        for feature_name, importance in weather_importance.items():
            if importance > self.impact_thresholds['high']:
                impact_levels[feature_name] = '高'
            elif importance > self.impact_thresholds['medium']:
                impact_levels[feature_name] = '中等'
            elif importance > self.impact_thresholds['low']:
                impact_levels[feature_name] = '较低'
            else:
                impact_levels[feature_name] = '低'
        
        return impact_levels
    
    def _get_target_date_weather(self, target_date: str) -> Dict:
        """获取目标日期的天气数据"""
        try:
            from backend.config.database import get_db_session
            from backend.entities.weather_daily import WeatherDaily
            
            target_dt = datetime.strptime(target_date, '%Y-%m-%d').date()
            
            with get_db_session() as db:
                weather_data = db.query(WeatherDaily).filter(
                    WeatherDaily.dt == target_dt
                ).first()
                
                if weather_data:
                    return {
                        't_max': weather_data.t_max,
                        't_min': weather_data.t_min,
                        'humidity': weather_data.humidity,
                        'wind_speed': weather_data.ws_max,
                        'precipitation': weather_data.precip
                    }
                else:
                    return {}
                    
        except Exception as e:
            logger.error(f"获取目标日期天气数据失败: {e}")
            return {}
    
    def _generate_analysis_report(self, weather_importance: Dict[str, float], 
                                weather_impact: Dict[str, str], 
                                weather_data: Dict) -> Dict:
        """生成详细的分析报告"""
        
        # 计算综合影响程度
        avg_importance = np.mean(list(weather_importance.values()))
        
        if avg_importance > self.impact_thresholds['high']:
            overall_impact = '高'
        elif avg_importance > self.impact_thresholds['medium']:
            overall_impact = '中等'
        elif avg_importance > self.impact_thresholds['low']:
            overall_impact = '较低'
        else:
            overall_impact = '低'
        
        # 生成详细报告
        report = {
            'weather_analysis': {
                'temperatureImpact': weather_impact.get('temp_max', '较低'),
                'humidityImpact': weather_impact.get('humidity', '较低'),
                'precipitationImpact': weather_impact.get('precip', '较低'),
                'windSpeedImpact': '较低'      # 暂时固定
            },
            'feature_importance': {
                'temp_max': round(weather_importance.get('temp_max', 0) * 100, 2),
                'temp_min': round(weather_importance.get('temp_min', 0) * 100, 2),
                'humidity': round(weather_importance.get('humidity', 0) * 100, 2),
                'precip': round(weather_importance.get('precip', 0) * 100, 2)
            },
            'analysis_summary': {
                'overall_impact': overall_impact,
                'avg_importance': round(avg_importance * 100, 2),
                'most_important_factor': max(weather_importance.items(), key=lambda x: x[1])[0] if weather_importance else 'none',
                'analysis_method': 'SHAP/LIME/FI',
                'analysis_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            },
            'weather_data': weather_data
        }
        
        return report
    
    def _get_default_weather_analysis(self) -> Dict:
        """获取默认的气象影响分析"""
        return {
            'weather_analysis': {
                'temperatureImpact': '中等',
                'humidityImpact': '较低',
                'precipitationImpact': '较低',
                'windSpeedImpact': '较低'
            },
            'feature_importance': {
                'temp_max': 8.5,
                'temp_min': 6.2,
                'humidity': 4.1,
                'precip': 2.3
            },
            'analysis_summary': {
                'overall_impact': '中等',
                'avg_importance': 5.28,
                'most_important_factor': 'temp_max',
                'analysis_method': '默认分析',
                'analysis_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            },
            'weather_data': {},
            'feature_importance_data': []
        }
    
    def get_enhanced_weather_analysis(self, model_name: str, target_date: str) -> Dict:
        """获取增强的气象影响分析（包含详细的影响程度判断）"""
        analysis = self.analyze_feature_importance(model_name, target_date)
        
        # 添加影响程度的详细说明
        impact_descriptions = {
            '高': '该气象因素对负荷预测有显著影响，变化会导致负荷大幅波动',
            '中等': '该气象因素对负荷预测有一定影响，需要重点关注',
            '较低': '该气象因素对负荷预测影响较小，但仍需考虑',
            '低': '该气象因素对负荷预测影响很小，可以忽略'
        }
        
        # 为每个气象因素添加详细说明
        for factor in ['temperatureImpact', 'humidityImpact', 'precipitationImpact', 'windSpeedImpact']:
            impact_level = analysis['weather_analysis'][factor]
            analysis['weather_analysis'][f"{factor}_description"] = impact_descriptions.get(impact_level, '影响程度未知')
        
        return analysis 